module Wondercap
module CapistranoMixins  

  module Variables
  
    def self.included(base)
      base.send :alias_method, :set_without_wondercap_variables_mixin, :set
      base.send :alias_method, :set, :set_with_wondercap_variables_mixin

      base.send :alias_method, :fetch_without_wondercap_variables_mixin, :fetch
      base.send :alias_method, :fetch, :fetch_with_wondercap_variables_mixin

      base.send :alias_method, :reset_without_wondercap_variables_mixin!, :reset!
      base.send :alias_method, :reset!, :reset_with_wondercap_variables_mixin!
    end
    
    def set?(variable, *args, &block)
      set(variable, *args, &block) unless exists?(variable)
    end
    
    def set_with_wondercap_variables_mixin(variable, *args, &block)
      if args.length == 2
        options_hash = args.pop
      end
      set_without_wondercap_variables_mixin(variable, *args, &block)

      if options_hash
        raise ArgumentError, "invalid argument where options hash expected" unless options_hash.respond_to? :rehash

        if options_hash.has_key? :on_fetch
          var_sym = variable.to_sym
          protect_first_fetch_assoc(var_sym) { first_fetch_associations[var_sym] = options_hash[:on_fetch] }
        end
      end
    end

    def first_fetch_associations
      @first_fetch_associations ||= {}
    end
    private :first_fetch_associations

    def used_first_fetch_associations
      @used_first_fetch_associations ||= {}
    end
    private :used_first_fetch_associations

    @@first_fetch_global_lock = Monitor.new
    def protect_first_fetch_assoc(sym)
      @@first_fetch_global_lock.synchronize do
        @first_fetch_assocs_locks ||= Hash.new { |h,k| h[k] = Monitor.new }
        @first_fetch_assocs_locks[sym]
      end.synchronize { yield }
    end
    private :protect_first_fetch_assoc

    def fetch_with_wondercap_variables_mixin(name, *args, &block)
      sym = name.to_sym

      protect_first_fetch_assoc(sym) do
        if first_fetch_task_name = first_fetch_associations[sym]
          used_first_fetch_associations[sym] = first_fetch_associations.delete sym
          logger.debug "triggering on-first-fetch task: #{name} => #{first_fetch_task_name.to_s}"
          find_and_execute_task(first_fetch_task_name)
        end
      end

      fetch_without_wondercap_variables_mixin(name, *args, &block)
    end

    def reset_with_wondercap_variables_mixin!(name)
      sym = name.to_sym
      protect_first_fetch_assoc(sym) do
        first_fetch_associations[sym] ||= used_first_fetch_associations[sym]
      end
      reset_without_wondercap_variables_mixin!(name)
    end  

  end

end  
end